package model.primitive;

import java.util.ArrayList;
import java.util.List;

import javax.vecmath.Point2f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import model.ray.Ray;
import model.shader.Shader;
import model.utils.Putils;

public class CopyOfTriangleOld extends Primitive implements Comparable<CopyOfTriangleOld> {
	private Point3d[] vertices;
	private Vector3d normal; /* Precomputo de la normal al triangulo */
	
	public Vector3d[] normals= new Vector3d[3];
	
	private Plane trianglePlane;
	private Point3d center = null;		//Centro del triangulo
	private double distance = 0.0;		//Distancia a la camara
	
	public CopyOfTriangleOld(String name, Shader shader, Point3d v0, Point3d v1, Point3d v2) {
		super(shader, name);
		
		if (v0 == null || v1 == null || v2 == null) {
			throw new IllegalArgumentException("null arguments recieved");
		}
		if (v0.equals(v1) || v0.equals(v2) || v1.equals(v2)) {
			throw new IllegalArgumentException("Vertex coordinates must be different among each other");
		}
		
		this.vertices = new Point3d[3];
		this.vertices[0] = new Point3d(v0);
		this.vertices[1] = new Point3d(v1);
		this.vertices[2] = new Point3d(v2);
		auxCalc();
	}
	
	public CopyOfTriangleOld(String name, Shader shader, Point3d[] vertices) {
		super(shader, name);
		
		if (vertices.length != 3) {
			throw new IllegalArgumentException("Exactly 3 vertices are required");
		}

		if (vertices[0].equals(vertices[1]) || vertices[0].equals(vertices[2]) || vertices[1].equals(vertices[2])) {
			throw new IllegalArgumentException("Exactly 3 vertices are required");
		}
		
		this.vertices = new Point3d[3];
		this.vertices[0] = new Point3d(vertices[0]);
		this.vertices[1] = new Point3d(vertices[1]);
		this.vertices[2] = new Point3d(vertices[2]);
		auxCalc();
	}
	
	@Override
	public List<Point3d> getIntersections(Ray ray) {
		double missCondition;
		int index[] = {1, 2, 0}; /* indices para el ciclo for */
		
		Point3d interPoint = null; /* Punto de interseccion entre el plano del triangulo y el rayo */
		Vector3d u,v;
		
		missCondition = 0;
		
		List<Point3d> intersections = new ArrayList<Point3d>();
		intersections = this.trianglePlane.getIntersections(ray); 
		if( intersections != null && intersections.size() > 0 )
			interPoint = intersections.get(0);
		
		if (interPoint == null) {
			return null; // no intersecta ni con el plano del triangulo
		}
		
		/* 
		 * Metodo de los medios planos (half-plane) */
		
		u = new Vector3d();
		v = new Vector3d();
		
		
		for (int i = 0 ; i < this.vertices.length && missCondition >= 0 ; i++) {
			u.sub(this.vertices[index[i]], this.vertices[i]); /* Vector que representa 1 lado del triangulo */
			v.sub(interPoint, this.vertices[i]);
			u.cross(u, v);
			missCondition = u.dot(this.normal);
		}
		
		if (missCondition < 0) {
			return null;
		}
		ArrayList<Point3d> ret = new ArrayList<Point3d>();
		ret.add(interPoint);
		return ret;
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		for (Point3d p: this.vertices) {
			sb.append(p).append(" ");
			
		}
		
		return sb.toString();
	}
	
	private void calcTriangleNormal() {
		
		Vector3d u = new Vector3d();
		Vector3d v = new Vector3d();
		
		u.sub(vertices[0],vertices[1]);
		v.sub(vertices[0],vertices[2]);
		
		u.cross(u, v);
			
		this.normal = u;

	}

	@Override
	public void translate(Point3d pos) {
		if( pos != null )	{
			this.vertices[0].add(pos);
			this.vertices[1].add(pos);
			this.vertices[2].add(pos);
			calcTriangleNormal();
			this.trianglePlane = new Plane(this.getShader(), this.getName(), this.vertices[0], this.normal);
		}
	}

	public Point3d[] getVertex() {
		return this.vertices;
	}

	@Override
	public Vector3d getNormal(Point3d pos) {
		
//		Point3d va= this.vertices[0];
//		Point3d vb= this.vertices[1];
//		Point3d vc= this.vertices[2];
//		
//		Vector3d na=this.normals[0];
//		Vector3d nb=this.normals[1];
//		Vector3d nc=this.normals[2];
//
//		Vector3d u = new Vector3d();
//		Vector3d v = new Vector3d();
//		Vector3d n = new Vector3d();
//		
//		u.sub(va,vb);
//		v.sub(vc,vb);
//		n.sub(pos,vb);
//		
//        double dU = u.length();
//        double dV = v.length();
//        double dN = n.length();
//
//        n.normalize();
//        u.normalize();
//        // v no normalizo ??
//
//        double cost = n.dot(u);
//        if (cost < 0) cost = 0;
//        if (cost > 1) cost = 1;
//
//        double t = Math.acos(cost);
//
//        double distY = 0, distX = 0;
//        distX = dN * Math.cos(t);
//        distY = dN * Math.sin(t);
//
//        double u2 = distX / dU;
//        double v2 = distY / dV;
//
//        na.normalize(); // normalizo cuando calculo las normales
//        nb.normalize();
//        nc.normalize();
//
//        double nx = -((1.0 - (u2 + v2)) * nb.x + na.x * u2 + nc.x * v2);
//        double ny = -((1.0 - (u2 + v2)) * nb.y + na.y * u2 + nc.y * v2);
//        double nz = -((1.0 - (u2 + v2)) * nb.z + na.z * u2 + nc.z * v2);
//
//        Vector3d nn =  new Vector3d(nx,ny,nz);
////      nn.normalize();
//        return nn;


// 		version original  ...
		
		Vector3d u = new Vector3d();
		Vector3d v = new Vector3d();
		
		u.sub(pos,vertices[0]);
		v.sub(pos,vertices[1]);
		
		u.cross(u, v);
// deber�a devolver una normal cualquiera por ejemplo
//		normals[0];
		u.normalize();
		return u;
		
	}

	

	@Override
	public void rotatex(Double d) {
		if(d!=null){
			this.vertices=Putils.rotatex(this.vertices,d);
			auxCalc();
		}
	}

	@Override
	public void rotatey(Double d) {
		if(d!=null){
			this.vertices=Putils.rotatey(this.vertices,d);
			auxCalc();
		}
	}

	@Override
	public void rotatez(Double d) {
		if(d!=null){
			this.vertices=Putils.rotatez(this.vertices,d);
			auxCalc();
		}
	}

	@Override
	public void scalex(Double s) {
		if(s!=null && s>0){
			this.vertices[0]=Putils.scalex(this.vertices[0],s);
			this.vertices[1]=Putils.scalex(this.vertices[1],s);
			this.vertices[2]=Putils.scalex(this.vertices[2],s);
			auxCalc();
		}
	}

	@Override
	public void scaley(Double s) {
		if(s!=null && s>0){
			this.vertices[0]=Putils.scaley(this.vertices[0],s);
			this.vertices[1]=Putils.scaley(this.vertices[1],s);
			this.vertices[2]=Putils.scaley(this.vertices[2],s);
			auxCalc();
		}
	}

	@Override
	public void scalez(Double s) {
		if(s!=null && s>0){
			this.vertices[0]=Putils.scalez(this.vertices[0],s);
			this.vertices[1]=Putils.scalez(this.vertices[1],s);
			this.vertices[2]=Putils.scalez(this.vertices[2],s);
			auxCalc();
		}
	}

	@Override
	public void scaleu(Double s) {
		if (s!=null && s>0){

			this.vertices[0].scale(s);
			this.vertices[1].scale(s);
			this.vertices[2].scale(s);
			auxCalc();
		}
	}
	
	private void auxCalc(){
		calcTriangleNormal();
		this.trianglePlane = new Plane(this.getShader(), this.getName(), this.vertices[0], this.normal);
	}
	
	
	public Point3d getMapColor( Point3d p ){
		
		Point3d color = new Point3d();
		
        double dx = 1;//imgBitmap.Width;
        double dy = 1;//imgBitmap.Height;
        
        Vector3d u = new Vector3d();
        Vector3d v = new Vector3d();
        Vector3d v2 = new Vector3d();

        Point3d p1 = vertices[0];
        Point3d p2 = vertices[1];
        Point3d p3 = vertices[2];
        
        u.sub(p3,p2);
        v2.sub(p,p2);
        
        Vector3d aux = new Vector3d();
        aux.sub(p2,p);
        double distp =  aux.length();
        v.sub(p1,p2);

        double du = u.length();
        double dv = v.length();
        
        u.normalize();
        v2.normalize();
        
        double cost = u.dot(v2);
        double t = Math.acos(cost);
        
        double distY = du - distp * Math.cos(t);
        double distX = dv - distp * Math.sin(t);
        
       
//	DONDE ESTA EL MALDITO TMP3 ??
        
//        double y1 = Algebra.getCoord(0, du, tmp3y * dy, tmp2y * dy, distY);
//        double x1 = Algebra.getCoord(0, dv, tmp1x * dx, tmp2x * dx, distX);
//        
//        int i1 = (int)x1, j1 = (int)y1;
//        if (i1 >= 0 && j1 >= 0 &&
//            i1 < imgBitmap.Width &&
//            j1 < imgBitmap.Height)
//        {
//            Color clr = imgBitmap.GetPixel(i1, j1);
//            color.x = clr.R;
//            color.y = clr.G;
//            color.z = clr.B;
//        }
        return color;
	}

	@Override
	public Point2f getUV(Point3d p) {
		
		Point3d color = new Point3d();
		
        Vector3d u = new Vector3d();
        Vector3d v = new Vector3d();
        Vector3d v2 = new Vector3d();

        Point3d p1 = vertices[0];
        Point3d p2 = vertices[1];
        Point3d p3 = vertices[2];
        
        u.sub(p3,p2);
        v2.sub(p,p2);
        
        Vector3d aux = new Vector3d();
        
        aux.sub(p2,p);
        v.sub(p1,p2);
        
        return null;
//        return new Point2f(u,v);
	}

	@Override
	public int compareTo(CopyOfTriangleOld triangle) {
		int ret;
		if (this.distance > triangle.distance) {
			ret = 1;
		} else if (this.distance < triangle.distance) {
			ret = -1;
		} else {
			ret = 0;
		}
		return ret;
	}

	public void setDistance(Vector3d vector) {
		if( this.center == null ) {
			this.setCenter();
		}
		Vector3d aux = new Vector3d();
		aux.sub(this.center, vector);
		this.distance = aux.length();
	}

	private void setCenter() {
		Point3d center = new Point3d();
		center.set( (vertices[0].x + vertices[1].x + vertices[2].x) / 3, 
					(vertices[0].y + vertices[1].y + vertices[2].y) / 3,
					(vertices[0].z + vertices[1].z + vertices[2].z) / 3 );
		this.center = center;
	}

	public Point3d getDmin() {
		Point3d min = new Point3d();
		min.x = Math.min(Math.min(vertices[0].x, vertices[1].x), vertices[2].x);
		min.y = Math.min(Math.min(vertices[0].y, vertices[1].y), vertices[2].y);	
		min.z = Math.min(Math.min(vertices[0].z, vertices[1].z), vertices[2].z);
		return min;
	}

	public Point3d getDmax() {
		Point3d max = new Point3d();
		max.x = Math.max(Math.max(vertices[0].x, vertices[1].x), vertices[2].x);
		max.y = Math.max(Math.max(vertices[0].y, vertices[1].y), vertices[2].y);	
		max.z = Math.max(Math.max(vertices[0].z, vertices[1].z), vertices[2].z);
		return max;
	}


}

